﻿using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Xsl;
using AuthoritExtensibility;
using API = Authorit.API;
using Dto = Authorit.API.Dto;

namespace ImporterForm
{
    /// <summary>
    /// Implements the user interface for the plug-in
    /// </summary>
    public partial class ImporterForm : Form
    {
        #region Fields (37)

        /// <summary>
        /// Stores configuration settings
        /// </summary>
        private Config _config;

        /// <summary>
        /// Stores the alias for the Author-it library
        /// </summary>
        private string alias = "Library";  //TODO: Move "Library" value to a configuration setting

        // Services
        /// <summary>
        /// Instance of the <see cref="API.Services.Authentication"/> class
        /// </summary>
        private API.Services.Authentication authentication = null;

        private ResourceManager rm;

        private AitApplicationProxy appProxy;
        private bool book = false;

        // Data transfer objects
        private Dto.Book bookDto = null;

        private API.Services.Book bookservice = null;
        private Dto.BookTocEntry bookTocEntryDto = null;

        // Library, application
        private API.Configuration.LibraryConfigurationList config = null;

        private API.ApplicationContext context = null;
        private XNamespace db = null;

        // name of importButton file
        private string description = string.Empty;

        private StringBuilder doc = null;
        private XElement document = null;
        private AuthoritEventArgs eventArgs;

        // Files
        private string filename = string.Empty;

        private Dto.Folder folderDto = null;
        private API.Services.Library libraryservice = null;

        // description of topic
        private string line = string.Empty;

        private DateTime now = DateTime.Now;
        private XmlReaderSettings readersettings = new XmlReaderSettings();
        private XElement remove = null;
        private Dto.LoginResponse response = null;
        private StreamReader stream = null;
        private XElement text = null;
        private Dto.Topic topicDto = null;

        // Building documents
        private XDocument topics = null;

        private API.Services.Topic topicservice = null;

        // XSLT file
        // Flags
        private bool txt = false;

        private Dto.VariableAssignment varassign = null;
        private Dto.Variable varDto = null;
        private API.Services.Variable varservice = null;
        private XmlWriter writer = null;
        private XmlWriterSettings writersettings = null;

        // lines of plain text file
        private string xfile = string.Empty;

        // xslt reader
        private XslCompiledTransform xslt = new XslCompiledTransform();

        // xslt stream (from resource)
        private XmlReader xsltreader = null;

        private MemoryStream xsltstream = null;

        #endregion Fields

        #region Constructors (2)

        /// <summary>
        /// Initializes a new instance of the <see cref="ImporterForm"/> class.
        /// </summary>
        /// <param name="args">Arguments for the plug-in</param>
        /// <param name="proxy">Application proxy for the plug-in</param>
        public ImporterForm(AuthoritEventArgs args, AitApplicationProxy proxy)
        {
            _config = new Config();
            //rm = new ResourceManager("ImporterForm.Default", Assembly.GetExecutingAssembly());
            //rm = Properties.Resources.ResourceManager;
            rm = new ResourceManager("ImporterForm.Properties.Resources", this.GetType().Assembly);
            
            this.eventArgs = args;
            this.appProxy = proxy;

            InitializeComponent();

            this.FormBorderStyle = _config.MainFormBorderStyle;
            this.MaximizeBox = _config.MainFormMaximizeBox;
            this.StartPosition = _config.MainFormStartPosition;

            // Jet or SQL Server?
            if (args.LibraryPath != null)
            {
                config = new API.Configuration.LibraryConfigurationList(new API.Configuration.JetLibraryConfiguration(alias, args.LibraryPath));
                this.Text = "XML Importer | Jet library: " + Path.GetFileName(args.LibraryPath);  //TODO: Move "XML Importer | Jet library: " value to a resource
            }
            else if (args.SqlServername != null)
            {
                config = new API.Configuration.LibraryConfigurationList(new API.Configuration.SqlServerLibraryConfiguration(alias, args.SqlServername, args.SqlDatabase));
                this.Text = "XML Importer | SQL Server library: " + args.SqlServername;  //TODO: Move "XML Importer | SQL Server library: " value to a resource
            }

            // Service instances
            context = new API.ApplicationContext(config);
            authentication = context.GetServiceInstance<API.Services.Authentication>();
            libraryservice = context.GetServiceInstance<API.Services.Library>();
            topicservice = context.GetServiceInstance<API.Services.Topic>();
            bookservice = context.GetServiceInstance<API.Services.Book>();
            varservice = context.GetServiceInstance<API.Services.Variable>();

            try
            {
                // Login
                response = authentication.Login(alias, args.Username, args.Password);
                this.infoLabel.Text = "Login: " + response.Result.ToString();  //TODO: Move "Login: " value to a resource
            }
            catch (API.Exceptions.AuthenticationException ex)
            {
                MessageBox.Show("Authentication Exception: " + ex.Message);
            }
        }

        // Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="ImporterForm"/> class.
        /// </summary>
        public ImporterForm()
        {
            InitializeComponent();
        }

        #endregion Constructors

        #region Methods (8)

        // Public Methods (2) 

        /// <summary>
        /// Refreshes the current folder in the Author-it UI
        /// </summary>
        public void RefreshFolder()
        {
            LibraryRefreshArgs refreshArgs = new LibraryRefreshArgs();
            refreshArgs.Type = LibraryRefreshType.FolderRefresh;
            this.appProxy.RefreshLibrary(refreshArgs);
        }

        /// <summary>
        /// Initializes the dialog
        /// </summary>
        public void Reset()
        {
            this.infoLabel.Text = "Done with import";  //TODO: Move "Done with import" value to a resource
            this.browse.Enabled = false;
            this.browseLabel.Enabled = false;
            this.browseTextBox.Enabled = false;
            this.bookTextBox.Enabled = false;
            this.bookCheckBox.Enabled = false;
            this.bookCheckBox.Checked = false;
            this.bookTextBox.Text = "Enter book name";  //TODO: Move "Enter book name" value to a resource
            this.importButton.Enabled = false;
            this.browseTextBox.Text = string.Empty;
            this.comboBox.SelectedIndex = -1;
            this.comboBox.Text = "Choose file format...";  //TODO: Move "Choose file format..." value to a resource
        }

        // Private Methods (6) 

        // Enable book text box when check box checked
        /// <summary>
        /// Handles the click event for the <see cref="bookCheckBox"/> control.
        /// </summary>
        /// <param name="sender">Object that initiated the event</param>
        /// <param name="e">Arguments for the event</param>
        /// <remarks>This enables or disables creating a book when importing.</remarks>
        private void bookCheckBox_Click(object sender, EventArgs e)
        {
            if (bookCheckBox.Checked == true)
            {
                this.bookTextBox.Enabled = true;
            }
            else if (bookCheckBox.Checked == false)
            {
                this.bookTextBox.Text = "Enter book name";  //TODO: Move "Enter book name" value to a resource
                this.bookTextBox.Enabled = false;
            }
        }

        /// <summary>
        /// Handles the click event for the <see cref="bookTextBox"/> control.
        /// </summary>
        /// <param name="sender">Object that initiated the event</param>
        /// <param name="e">Arguments for the event</param>
        /// <remarks>This clears the default text from the <see cref="bookTextBox"/> control so the user does not have select and delete it manually.</remarks>
        private void bookTextBox_Click(object sender, EventArgs e)
        {
            this.bookTextBox.Clear();
        }

        /// <summary>
        /// Handles the click event for the <see cref="browse"/> control.
        /// </summary>
        /// <param name="sender">Object that initiated the event</param>
        /// <param name="e">Arguments for the event</param>
        /// <remarks>This displays the <see cref="openFileDialog"/> dialog and allows the user to select a file to import.</remarks>
        private void browse_Click(object sender, EventArgs e)
        {
            openFileDialog.ShowDialog();  //TODO: Implement code to determine if the user selected a file or cancelled the dialog
            browseTextBox.Text = openFileDialog.FileName;
            this.importButton.Enabled = true;
            this.bookCheckBox.Enabled = true;

            try
            {
                document = XElement.Load(openFileDialog.FileName); // Test text file (is it XML or not)?

                // Get the namespace if DocBook namespace present
                if (document.GetDefaultNamespace() == _config.DocBookDefaultNamespace)
                    db = document.GetDefaultNamespace();
            }
            catch (Exception ex)
            {
                this.infoLabel.Text = "Plain Text File: " + ex.Message;  //TODO: Move "Plain Text File: " value to a resource
            }
        }

        /// <summary>
        /// Handles the click event for the <see cref="close"/> control.
        /// </summary>
        /// <param name="sender">Object that initiated the event</param>
        /// <param name="e">Arguments for the event</param>
        /// <remarks>This closes the <see cref="ImporterForm"/> dialog.</remarks>
        private void close_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Handles the selected index changed event for the <see cref="comboBox"/> control.
        /// </summary>
        /// <param name="sender">Object that initiated the event</param>
        /// <param name="e">Arguments for the event</param>
        /// <remarks>The table below describes the possible values.
        /// <list type="table">
        ///     <listheader>
        ///         <term>Value</term>
        ///         <description>Description</description>
        ///     </listheader>
        ///     <item>
        ///         <term>0</term>
        ///         <description>DITA</description>
        ///     </item>
        ///     <item>
        ///         <term>1</term>
        ///         <description>DocBook</description>
        ///     </item>
        ///     <item>
        ///         <term>2</term>
        ///         <description>Arbitrary XML</description>
        ///     </item>
        ///     <item>
        ///         <term>3</term>
        ///         <description>Plain text</description>
        ///     </item>
        /// </list></remarks>
        private void comboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            switch (comboBox.SelectedIndex)
            {
                case 0:
                    this.infoLabel.Text = "File type: DITA";  //TODO: Move "File type: DITA" value to a resource
                    this.browse.Enabled = true;
                    this.browseLabel.Enabled = true;
                    this.browseTextBox.Enabled = true;
                    txt = false;
                    xsltstream = new MemoryStream(Encoding.UTF8.GetBytes(Properties.Resources.DitaToAit)); //TODO: Move the DitaToAit to an external file
                    readersettings.ProhibitDtd = false;
                    xsltreader = XmlReader.Create(xsltstream, readersettings);
                    break;

                case 1:
                    this.infoLabel.Text = "File type: DocBook";  //TODO: Move "File type: DocBook" value to a resource
                    this.browse.Enabled = true;
                    this.browseLabel.Enabled = true;
                    this.browseTextBox.Enabled = true;
                    txt = false;
                    xsltstream = new MemoryStream(Encoding.UTF8.GetBytes(Properties.Resources.DbToAit));
                    readersettings.ProhibitDtd = false;
                    xsltreader = XmlReader.Create(xsltstream, readersettings);
                    break;

                case 2:
                    this.infoLabel.Text = "File type: Arbitrary XML";  //TODO: Move "File type: Arbitrary XML" value to a resource
                    this.browse.Enabled = true;
                    this.browseLabel.Enabled = true;
                    this.browseTextBox.Enabled = true;
                    txt = false;
                    xsltstream = new MemoryStream(Encoding.UTF8.GetBytes(Properties.Resources.ArbitraryXmlToAit));
                    readersettings.ProhibitDtd = false;
                    xsltreader = XmlReader.Create(xsltstream, readersettings);
                    break;

                case 3:
                    this.infoLabel.Text = "File type: Plain text";  //TODO: Move "File type: Plain text" value to a resource
                    this.browse.Enabled = true;
                    this.browseLabel.Enabled = true;
                    this.browseTextBox.Enabled = true;
                    txt = true;
                    break;

                default:
                    this.infoLabel.Text = "Ready to import another file";  //TODO: Move "Ready to import another file" value to a resource
                    break;
            }
        }

        /// <summary>
        /// Handles the click event of the <see cref="importButton"/> control.
        /// </summary>
        /// <param name="sender">Object that initiated the event</param>
        /// <param name="e">Arguments for the event</param>
        /// <remarks>This performs the importing and creation of objects in the library.</remarks>
        private void importButton_Click(object sender, EventArgs e)
        {
            // What file are you importing?
            this.infoLabel.Text = "File: " + Path.GetFileName(openFileDialog.FileName);  //TODO: Move "File: " value to a resource

            // Grab topic content, depending on file format
            this.infoLabel.Text = String.Empty;
            if (!txt)
            {
                doc = new StringBuilder();
                try
                {
                    xslt.Load(xsltreader);
                    writersettings = new XmlWriterSettings();
                    writersettings.Indent = true;
                    writersettings.OmitXmlDeclaration = true;
                    writersettings.ConformanceLevel = ConformanceLevel.Auto;
                    writersettings.CloseOutput = false;
                    writer = XmlWriter.Create(doc, writersettings);
                    xslt.Transform(openFileDialog.FileName, writer);
                    writer.Close();
                }
                catch (XsltException ex)
                {
                    MessageBox.Show("XSLT Compile Exception: " + ex.Message);
                }
            }
            else if (txt)
            {
                try
                {
                    stream = new StreamReader(openFileDialog.FileName);
                }
                catch (IOException ex)
                {
                    MessageBox.Show("Text file stream exception: " + ex.Message);
                }
                description = stream.ReadLine();
                text = new XElement(_config.AitXmlElementNameText);
                while ((line = stream.ReadLine()) != null)
                {
                    if (line != string.Empty)
                    {
                        text.Add(new XElement(_config.AitXmlElementNameParagraph, _config.DefaultParagraphStyleId, line));
                    }
                }
                stream.Close();
            }

            // Set folder to current folder
            folderDto = new Dto.Folder();
            folderDto.FolderId = eventArgs.CurrentFolderId;

            // Create book (optional)
            if (this.bookCheckBox.Checked == true)
            {
                book = true;
                try
                {
                    bookDto = new Dto.Book();
                    bookDto.FolderId = eventArgs.CurrentFolderId;
                    bookDto.Description = this.bookTextBox.Text;
                    bookDto = bookservice.Create(bookDto);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Book Exception: " + ex.Message); //TODO: Move "Book Exception" value to a resource
                }
            }

            // For plain text
            if (txt)
            {
                // Set up new topic
                topicDto = new Dto.Topic();
                topicDto.Description = description;
                topicDto.HelpHeading = description;
                topicDto.PrintHeading = description;
                topicDto.WebHeading = description;
                topicDto.FolderId = folderDto.FolderId;
                try
                {
                    topicDto = topicservice.Create(topicDto);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Create Topic Exception: " + ex.Message);
                }

                // Save topic
                try
                {
                    topicservice.SaveTopicTextFromXml(topicDto.ObjectId, text.ToString());
                    if (book)
                    {
                        bookTocEntryDto = new Dto.BookTocEntry();
                        bookTocEntryDto.ObjectId = topicDto.ObjectId;
                        bookDto.TableOfContents.Add(bookTocEntryDto);
                        bookservice.Update(bookDto);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Save Topic Exception: " + ex.Message);
                }

                // Set up variables
                try
                {
                    varDto = new Dto.Variable();
                    varDto = varservice.GetByName(_config.VariableNameDcmiTitle);
                    varassign = new Dto.VariableAssignment();
                    varassign.ObjectId = topicDto.ObjectId;
                    varassign.VariableId = varDto.VariableId;
                    varassign.Value = description;
                    varservice.Assign(varassign);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Variable Exception: " + ex.Message + "\n" + ex.StackTrace);
                }

                // Reset dialog box
                Reset();
            }

            // For XML
            else
            {
                try
                {
                    topics = XDocument.Parse(doc.ToString());
                }
                catch (Exception ex)
                {
                    this.infoLabel.Text = ex.Message;
                }

                try
                {
                    // Process topics, folder (data transfer objects)
                    foreach (XElement el in topics.Descendants("Text"))
                    {
                        // Description or title of topic (remove from body)
                        description = el.Descendants().First().Value;
                        remove = el.Descendants().First();
                        remove.Remove();

                        // Set up new topic
                        topicDto = new Dto.Topic();
                        topicDto.Description = description;
                        topicDto.HelpHeading = description;
                        topicDto.PrintHeading = description;
                        topicDto.WebHeading = description;
                        topicDto.FolderId = folderDto.FolderId;

                        try
                        {
                            topicDto = topicservice.Create(topicDto);
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show("Create Topic Exception: " + ex.Message);
                        }

                        // Save topic
                        try
                        {
                            topicservice.SaveTopicTextFromXml(topicDto.ObjectId, el.ToString());
                            if (book)
                            {
                                bookTocEntryDto = new Dto.BookTocEntry();
                                bookTocEntryDto.ObjectId = topicDto.ObjectId;
                                bookDto.TableOfContents.Add(bookTocEntryDto);
                                bookservice.Update(bookDto);
                            }
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show("Save Topic Exception: " + ex.Message);
                        }

                        // Set up variables
                        try
                        {
                            varDto = new Dto.Variable();
                            varDto = varservice.GetByName(_config.VariableNameDcmiTitle);
                            varassign = new Dto.VariableAssignment();
                            varassign.ObjectId = topicDto.ObjectId;
                            varassign.VariableId = varDto.VariableId;
                            varassign.Value = description;
                            varservice.Assign(varassign);
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show("Variable Exception: " + ex.Message + "\n" + ex.StackTrace);
                        }

                        // Reset dialog box
                        Reset();
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Exception: " + ex.Message);
                }
            }
            RefreshFolder();
        }

        #endregion Methods
    }
}